
package com.ifcar.app.view;

import com.ifcar.app.util.VersionCompat;

import android.content.Context;
import android.graphics.PointF;
import android.graphics.RectF;
import android.support.v4.view.MotionEventCompat;
import android.util.FloatMath;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.ViewConfiguration;
import android.widget.Scroller;

public class ImageViewTouchDelegate {
    private static final int INVALID_VALUE = -1;

    private static final int NONE = 0;

    private static final int DOWN = NONE + 1;

    private static final int DRAG = DOWN + 1;

    private static final int ZOOM = DRAG + 1;

    private int mMode = NONE;

    private int mActivePointerId = INVALID_VALUE;

    private PointF mLastPoint = new PointF();

    private PointF mMid = new PointF();

    private float mLastDist = 1f;

    private float mLastScale = 1f;

    private VelocityTracker mVelocityTracker;

    private int mTouchSlop;

    private int mMinimumVelocity;

    private int mMaximumVelocity;

    private Scroller mScroller;

    private Runnable mScrollRunnable;

    public ImageViewTouchDelegate(Context context) {
        final ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
        mScroller = new Scroller(context);
    }

    public void computeScroll(final ImageViewTouch view) {
       
    }

    public boolean onTouch(final ImageViewTouch view, MotionEvent event) {
        final ImageViewer imageViewer = view.getImageViewer();

        if (!view.isReady()) {
            mMode = NONE;
            return callImageViewerTouch(view.getImageViewer(), event);
        }
        
        boolean hasInterpreterTouch = imageViewer != null && imageViewer.hasInterpreterTouch() ? callImageViewerTouch(
                imageViewer, event)
                : false;

        if (event.getAction() == MotionEvent.ACTION_DOWN && mVelocityTracker != null) {
            mVelocityTracker.clear();
            mVelocityTracker = null;
        }

        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);

        int edge = ImageViewTouch.TOP_EDGE | ImageViewTouch.LEFT_EDGE | ImageViewTouch.BOTTOM_EDGE | ImageViewTouch.RIGHT_EDGE;
        final int action = event.getAction();
        switch (action & MotionEventCompat.getActionMasked(event)) {
            case MotionEvent.ACTION_DOWN:
                endFling(view);
                mLastPoint.set(event.getX(), event.getY());
                mMode = DOWN;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
                if (VersionCompat.getMotionEventCompat().getPointerCount(event) == 2) {
                    mActivePointerId = MotionEventCompat.getPointerId(event, 0);
                    mMode = ZOOM;
                    mLastDist = spacing(event);
                    if (mLastDist > 10f) {
                        midPoint(mMid, event);
                    }
                }
                break;
            case MotionEvent.ACTION_POINTER_UP:
                onSecondaryPointerUp(event);
                if (VersionCompat.getMotionEventCompat().getPointerCount(event) == 2) {
                    final float scale = view.getScale();
                    if (scale < 1f) {
                        mLastScale = 1f;
                        view.zoomTo(1f, mMid.x, mMid.y, 200);
                    } else if (scale >= view.getMaxZoom()) {
                        mLastScale = view.getMaxZoom();
                        view.zoomTo(view.getMaxZoom(), mMid.x, mMid.y, 200);
                    }

                    mMode = DRAG;
                }

                break;
            case MotionEvent.ACTION_MOVE:
                if (mMode == DOWN) {
                    final float x = event.getX();
                    final float y = event.getY();
                    final float deltaX = x - mLastPoint.x;
                    final float deltaY = y - mLastPoint.y;

                    if (Math.abs(deltaX) > mTouchSlop || Math.abs(deltaY) > mTouchSlop) {
                        mMode = DRAG;
                    }
                    mLastPoint.set(x, y);
                }

                if (mMode == DRAG && !hasInterpreterTouch) {
                    final float x = event.getX();
                    final float y = event.getY();
                    edge = view.postTranslateCenter(x - mLastPoint.x, y - mLastPoint.y);
                    mLastPoint.set(x, y);
                } else if (mMode == ZOOM && !hasInterpreterTouch) {
                    float newDist = spacing(event);
                    if (newDist > 10f) {
                        mLastScale *= (newDist / mLastDist);
                        view.zoomTo(mLastScale, mMid.x, mMid.y);
                        mLastDist = newDist;
                    }
                    edge = ImageViewTouch.NONE;
                }
                break;
            case MotionEvent.ACTION_UP:
                if (mMode == DOWN) {
                    imageViewer.post(new Runnable() {
                        @Override
                        public void run() {
                            imageViewer.performItemClick();
                        }
                    });
                } else if (mMode == DRAG) {
                    mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    int xVelocity = (int) mVelocityTracker.getXVelocity();
                    int yVelocity = (int) mVelocityTracker.getYVelocity();
                    if (Math.abs(xVelocity) > mMinimumVelocity
                            || Math.abs(yVelocity) > mMinimumVelocity) {
                        if (Math.abs(xVelocity) <= mMinimumVelocity) {
                            xVelocity = 0;
                        }
                        if (Math.abs(yVelocity) <= mMinimumVelocity) {
                            yVelocity = 0;
                        }
                        fling(view, -xVelocity, -yVelocity);
                    } else {
                        view.center(true, true, 200);
                    }
                } else {
                    view.center(true, true, 200);
                }
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                mActivePointerId = INVALID_VALUE;
                mMode = NONE;
                break;
            case MotionEvent.ACTION_CANCEL:
                if (mVelocityTracker != null) {
                    mVelocityTracker.recycle();
                    mVelocityTracker = null;
                }
                mActivePointerId = INVALID_VALUE;
                mMode = NONE;
                view.center(true, true, 200);
                break;

        }
        
        final int e = imageViewer.isVertical() ? (ImageViewTouch.TOP_EDGE | ImageViewTouch.BOTTOM_EDGE)
                : (ImageViewTouch.LEFT_EDGE | ImageViewTouch.RIGHT_EDGE);

        final boolean dealed = (edge & e) == ImageViewTouch.NONE;

        if (!hasInterpreterTouch && !dealed) {
            callImageViewerTouch(imageViewer, event);
        } 

        return true;
    }

    private boolean callImageViewerTouch(ImageViewer imageViewer, MotionEvent event) {
        if (imageViewer != null) {
            return imageViewer.onTouch(event);
        }
        return false;
    }

    private void onSecondaryPointerUp(MotionEvent ev) {
        final int pointerIndex = MotionEventCompat.getActionIndex(ev);
        final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);
        if (pointerId == mActivePointerId) {
            // This was our active pointer going up. Choose a new
            // active pointer and adjust accordingly.
            // TODO: Make this decision more intelligent.
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            mLastPoint.set(MotionEventCompat.getX(ev, newPointerIndex), MotionEventCompat.getY(ev, newPointerIndex));
            mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);
            if (mVelocityTracker != null) {
                mVelocityTracker.clear();
            }
        }
    }

    private void fling(final ImageViewTouch view, int xVelocity, int yVelocity) {
        RectF r = view.getMapRect();
        int initialX = 0;
        int initialY = 0;
        // 当xVelocity >= 0时minX为0，当xVelocity < 0时minX为view宽-图片宽的负值(view宽<图片宽)
        int minX = xVelocity >= 0 ? 0 : -(int) Math.max(0, r.width() - view.getWidth());
        // 当xVelocity <= 0时maxX为0，当xVelocity > 0时minX为view宽-图片宽(view宽<图片宽)
        int maxX = xVelocity > 0 ? (int) Math.max(0, r.width() - view.getWidth()) : 0;
        // 同minX
        int minY = yVelocity > 0 ? 0 : -(int) Math.max(0, r.height() - view.getHeight());
        // 同maxX
        int maxY = yVelocity > 0 ? (int) Math.max(0, r.height() - view.getHeight()) : 0;
        mLastPoint.set(initialX, initialY);
        mScroller.fling(initialX, initialY, xVelocity, yVelocity, minX, maxX, minY, maxY);
        mScrollRunnable = new Runnable() {
            @Override
            public void run() {
                if (mScroller.computeScrollOffset()) {
                    final float transX = mScroller.getCurrX() - mLastPoint.x;
                    final float transY = mScroller.getCurrY() - mLastPoint.y;
                    mLastPoint.set(mScroller.getCurrX(), mScroller.getCurrY());
                    view.postTranslateCenter(-transX, -transY);
                    view.post(this);
                } else {
                    endFling(view);
                }
            }
        };
        view.post(mScrollRunnable);
        view.invalidate();
    }

    private void endFling(final ImageViewTouch view) {
        mScroller.forceFinished(true);
        if (mScrollRunnable != null) {
            view.removeCallbacks(mScrollRunnable);
            mScrollRunnable = null;
        }
    }

    private float spacing(MotionEvent event) {
        float x = MotionEventCompat.getX(event, 0) - MotionEventCompat.getX(event, 1);
        float y = MotionEventCompat.getY(event, 0) - MotionEventCompat.getY(event, 1);
        return FloatMath.sqrt(x * x + y * y);
    }

    private void midPoint(PointF point, MotionEvent event) {
        float x = MotionEventCompat.getX(event, 0) + MotionEventCompat.getX(event, 1);//event.getX(0) + event.getX(1);
        float y = MotionEventCompat.getY(event, 0) + MotionEventCompat.getY(event, 1);//event.getY(0) + event.getY(1);
        point.set(x / 2, y / 2);
    }
}
